#include "desktop-element.h"
void listbox_set_position(desktop_element *element);
struct Element array[] = {      
    { NAME_VIEW, ELEMENT_VIEW, desktop_element_set_view_fixed }, 
    { NAME_BUTTON, ELEMENT_BUTTON, desktop_element_set_view_button },
    { NAME_CHECKBOX, ELEMENT_CHECKBOX,  desktop_element_set_view_checkbox }, 
    { NAME_OBJECT, ELEMENT_OBJECT, NULL }, 
    { NAME_CONTENTAREA, ELEMENT_CONTENTAREA, NULL },
    { NAME_DIV, ELEMENT_DIV, desktop_element_set_view_div }, 
    { NAME_EDIT, ELEMENT_EDIT, desktop_element_set_view_edit }, 
    { NAME_IMG, ELEMENT_IMG, desktop_element_set_view_img }, 
    { NAME_LABEL, ELEMENT_LABEL, desktop_element_set_view_label }, 
    { NAME_A, ELEMENT_A, desktop_element_set_view_a }, 
    { NAME_PROGRESSBAR, ELEMENT_PROGRESSBAR, desktop_element_set_view_progressbar }, 
    { NAME_RADIO, ELEMENT_RADIO, desktop_element_set_view_radio },
    { NAME_LISTBOX, ELEMENT_LISTBOX, desktop_element_set_view_listbox }, 
    { NAME_LISTITEM, ELEMENT_LISTITEM, desktop_element_set_view_listitem }, 
    { NAME_SCRIPT, ELEMENT_SCRIPT, NULL }, 
    { NULL, 0, NULL } ,
};

desktop_element *desktop_element_new_with_name(const gchar *type,
					       const gchar *name, js_engine *engine) {
    desktop_element *new = (desktop_element *)g_new0(desktop_element, 1);
    new->type = g_strdup(type);
    new->name = g_strdup(name);
    new->attrs = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
    new->engine = engine;
    return new;
}

desktop_element *desktop_element_new(const gchar *type, js_engine *engine) {
    desktop_element *new = (desktop_element *)g_new0(desktop_element, 1);
    new->type = g_strdup(type);
    new->name = NULL;
    new->attrs = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
    new->engine = engine;
    return new;
}

void desktop_element_destroy(desktop_element *widget) {
    int i;
    desktop_element *child;
    gchar *text;
    g_free(widget->type);
    widget->type = NULL;
    g_free(widget->name);
    widget->name = NULL;
    g_hash_table_remove_all(widget->attrs);
    g_hash_table_destroy(widget->attrs);
    widget->attrs = NULL;

    for (i = 0; i < g_list_length(widget->children); i++) {
        child = (desktop_element *)g_list_nth_data(widget->children, i);
        desktop_element_destroy(child);
    }
    widget->children = NULL;
    for (i = 0; i < g_list_length(widget->text); i++) {
        text = (gchar *)g_list_nth_data(widget->text, i);
        g_free(text);
    }
    widget->text = NULL;
    if (widget->view)
        gtk_widget_destroy(widget->view);
    widget->jsobj = NULL;
}

void *desktop_element_get_private(desktop_element *widget){
    return widget->private;
}

void desktop_element_set_private(desktop_element *widget, gpointer *ptr){
    widget->private = ptr;
}

void desktop_element_set_name(desktop_element *widget, const gchar *name) {
    if (widget->name)
        g_free(widget->name);
    widget->name = g_strdup(name);
}

void desktop_element_set_attr(desktop_element *widget, const gchar *key,
			      const gchar *value) {
    gchar *inserted_key = g_strdup(key);
    gchar *inserted_value = g_strdup(value);
    g_hash_table_insert(widget->attrs, inserted_key, inserted_value);
}

gchar* desktop_element_get_attr(desktop_element *widget, const gchar *key) {
    jsval rval;
    g_warning("desktop_element_get_attr is deprecated.");
    if(widget->engine == NULL)
        g_error("js_engine is not initialized.");

    g_return_val_if_fail(JS_GetProperty(widget->engine->cx, widget->jsobj, key, &rval), NULL);
    if(JSVAL_IS_STRING(rval)){
        return g_strdup(JS_GetStringBytes(JSVAL_TO_STRING(rval)));
    }else if(JSVAL_IS_INT(rval)){
        return g_strdup_printf("%d", JSVAL_TO_INT(rval));
    }else if(JSVAL_IS_DOUBLE(rval)){
        jsdouble *value = JSVAL_TO_DOUBLE(rval);
        return g_strdup_printf("%f", *value);
    }else{
        return NULL;
    }
}

void desktop_element_add_child(desktop_element *parent, desktop_element *child) {
    parent->children = g_list_append(parent->children, child);
}

void desktop_element_set_text(desktop_element *widget, const gchar *text) {
    widget->text = g_list_append(widget->text, g_strdup(text));
}

gchar* desktop_element_get_text(desktop_element *widget) {
    if(g_list_length(widget->text) == 0)
        return NULL;
    return (gchar *)g_list_nth_data(widget->text,0);
}

void desktop_element_set_position(desktop_element *root, desktop_element *widget) {
    int i;
    for (i = 0; i < g_list_length(widget->children); i++) {
        desktop_element *child = g_list_nth_data(widget->children, i);
        if(child->view){
            gint x = 0, y = 0, height = -1, width = -1;
            jsval x_rval, y_rval, height_rval, width_rval, isVisible;

            JS_GetProperty(widget->engine->cx, child->jsobj, "x", &x_rval);
            JS_GetProperty(widget->engine->cx, child->jsobj, "y", &y_rval);
            if(JSVAL_IS_INT(x_rval))
                x = JSVAL_TO_INT(x_rval);
            if(JSVAL_IS_INT(y_rval))
                y = JSVAL_TO_INT(y_rval);
                        
            JS_GetProperty(widget->engine->cx, child->jsobj, "height", &height_rval);

            if(JSVAL_IS_INT(height_rval)){
                height = JSVAL_TO_INT(height_rval);
            }else if(JSVAL_IS_STRING(height_rval)){
                GString *height_str = g_string_new(JS_GetStringBytes(JSVAL_TO_STRING(height_rval)));
                if(height_str->str[height_str->len-1] == '%'){
                    jsval view_height;
                    guint height_percentage;
                    height_str->str[height_str->len-1] = '\0';
                    height_percentage = (guint)g_ascii_strtoull(height_str->str, NULL, 10);
                    JS_GetProperty(root->engine->cx, root->jsobj, "height", &view_height);
                    height = (guint)JSVAL_TO_INT(view_height)*height_percentage/100;
                }else{
                    height = (guint)g_ascii_strtoull(height_str->str, NULL, 10); 
                }
                g_string_free(height_str, TRUE);
            }else{
		jsval size;
		JS_GetProperty(widget->engine->cx, child->jsobj, "size", &size);
		if(JSVAL_IS_INT(size))
		    height = JSVAL_TO_INT(size);
	    }

            JS_GetProperty(widget->engine->cx, child->jsobj, "width", &width_rval);                        
            if(JSVAL_IS_INT(width_rval)){
                width = JSVAL_TO_INT(width_rval);
            }else if(JSVAL_IS_STRING(width_rval)){
                GString *width_str = g_string_new(JS_GetStringBytes(JSVAL_TO_STRING(width_rval)));
                if(width_str->str[width_str->len-1] == '%'){
                    jsval view_width;
                    guint width_percentage;
                    width_str->str[width_str->len-1] = '\0';
                    width_percentage =(guint)g_ascii_strtoull(width_str->str, NULL, 10);
                    JS_GetProperty(root->engine->cx, root->jsobj, "width", &view_width);
                    width = (guint)JSVAL_TO_INT(view_width)*width_percentage/100;
                }else{
                    width=(guint)g_ascii_strtoull(width_str->str, NULL, 10); 
                }
                g_string_free(width_str, TRUE);
            }
                        
            JS_GetProperty(widget->engine->cx, child->jsobj, "visible", &isVisible);
            if(JSVAL_IS_BOOLEAN(isVisible) && JSVAL_TO_BOOLEAN(isVisible) == JS_FALSE)
                gtk_widget_hide_all(child->view);
            else{
		gtk_widget_show_all(child->view);
	    }
	    gtk_widget_set_size_request(child->view, width, height);
            gtk_fixed_put(GTK_FIXED(root->view), child->view, x, y);
	    if(g_strcasecmp(child->type,NAME_LISTBOX) == 0){
		listbox_set_position(child);
	    }
        }
    }
}

JSBool desktop_element_call_handler(desktop_element *element, const gchar *handler_name){
    jsval handler, argv, rval;
    g_return_val_if_fail(JS_GetProperty(element->engine->cx, element->jsobj, 
					handler_name, &handler), JS_FALSE);

    if(JSVAL_IS_OBJECT(handler) && 
       JS_ObjectIsFunction(element->engine->cx, JSVAL_TO_OBJECT(handler))){
        return JS_CallFunctionValue(element->engine->cx, element->engine->glob, 
				    handler, 0, &argv, &rval);
    }else if(JSVAL_IS_STRING(handler)){
        GString *function = g_string_new(NULL);
        g_string_append(function, JS_GetStringBytes(JSVAL_TO_STRING(handler)));
        g_debug("evaluate handler %s", function->str);
        JS_EvaluateScript(element->engine->cx, element->jsobj, 
			  function->str, function->len, NULL, 0, &rval);
        g_string_free(function, TRUE);
    }else{
        if(JSVAL_IS_VOID(handler)){
            return JS_TRUE;
        }
    }
    return JS_FALSE;
}

gboolean desktop_element_set_view(js_engine *new_engine, desktop_element *widget) {
    struct Element *e = array;
    int i;

    g_return_val_if_fail(new_engine, FALSE);

    while (e->element_name) {
        if (g_ascii_strcasecmp(e->element_name, widget->type) == 0) {
            if (e->function){
                e->function(widget->engine, widget);
            }else {
                g_warning("NOT IMPLEMENTED : %s", widget->type);
            }
            break;
        }
        e++;
    }
    for (i = 0; i < g_list_length(widget->children); i++) {
        desktop_element *child = g_list_nth_data(widget->children, i);
        desktop_element_set_view(widget->engine, child);
    }
    return TRUE;
}

void desktop_element_set_view_fixed(js_engine *engine, desktop_element *widget) {
    widget->view = gtk_fixed_new();
    gtk_widget_show(widget->view);
}

gchar *trans_unix_path_new(const gchar *original) {
    gchar *new= NULL;
    gchar **parsed = g_strsplit(original, "\\", 0);
    new = g_build_pathv("/", parsed);
    g_strfreev(parsed);
    return new;
}

void listbox_set_position(desktop_element *element){
    GtkWidget *scrolled; 
    GtkWidget *viewport;
    GtkWidget *fixed;
    guint bottom = 0;
    int i;
    g_return_if_fail(element);
    g_return_if_fail(g_strcasecmp(element->type, "listbox") == 0);
 
    scrolled = element->view;
    viewport = gtk_bin_get_child(GTK_BIN(scrolled));
    fixed = gtk_bin_get_child(GTK_BIN(viewport));

    for(i = 0; i < g_list_length(element->children); i++){
	desktop_element *item = NULL;
	desktop_element *child = NULL;
	jsval width, height;
	guint width_val, height_val;

	item = (desktop_element *)g_list_nth_data(element->children, i);
	if(item == NULL)
	    break;
	if(g_list_length(item->children) != 1)
	    break;

	child = g_list_nth_data(item->children, 0);

	JS_GetProperty(element->engine->cx, item->jsobj, "width", &width);
	JS_GetProperty(element->engine->cx, item->jsobj, "height", &height);

	if(JSVAL_IS_INT(width))
	    width_val = JSVAL_TO_INT(width);
	if(JSVAL_IS_INT(height))
	    height_val = JSVAL_TO_INT(height);
	gtk_fixed_put(GTK_FIXED(fixed), item->view, 0, bottom);
	gtk_container_add(GTK_CONTAINER(item->view), child->view);
	bottom += height;
    }
    gtk_widget_show_all(fixed);
}
